Babel-使用手册

介绍

Babel 的用途:

  • 一个 JS 编译器,可以帮我们把最新标准编写的 JS 代码编译成向下兼容的 JS 代码,这样我们就可以在旧的浏览器环境中进行开发

  • 支持语法扩展,能支持像 React 所用的 JSX 语法

  • 支持用于静态类型检查的流式语法(Flow Syntax)

Babel 的一切都是简单的插件,谁都可以创建自己的插件,利用 Babel 的全部威力去做任何事情;自身被分解成了数个核心模块,任何人都可以利用它们来创建下一代的 JavaScript 工具。

@babel/core

Babel的核心模块,可以使用 transform 方法进行编译代码

1
2
3
4
$ npm i --save-dev @babel/core

const babel = require('@babel/core');
babel.transform("code", options);

@babel/cli

一个终端运行工具,内置的插件。一种在命令行下使用 Babel 编译文件的简单方法

1
2
// 安装
$ npm install --save-dev babel-cli

用法一:命令行的形式(在项目根目录执行语句)

解析 src 目录下的所有 JS 文件, 并将转换后的每个文件都输出到 lib 目录下

1
2
3
$ npx babel src --out-dir lib
#
$ npx babel src --d lib

解析指定的文件,并写入到指定的文件

1
2
3
$ npx babel example.js --out-file compiled.js
#
$ npx babel example.js -o compiled.js

用法二:package.json 配置

1
2
3
"scripts": {
"build": "babel src -d lib"
}

插件、Presets(预设)

Babel 默认情况是什么都不做,需要配置插件或者 presets(预设,一组插件)告诉 Babel 去做什么

插件本质就是一个JS程序, 指示着Babel如何对代码进行转换

插件选项

很多插件也有选项用于配置他们自身的行为。 例如,很多转换器都有“宽松”模式,通过放弃一些标准中的行为来生成更简化且性能更好的代码。

1
2
3
4
5
{
"plugins": [
["transform-es2015-classes", { "loose": true }]
]
}

一些 Presets

babel-preset-stage-x 实现阶段

JavaScript 还有一些提案,正在积极通过 TC39(ECMAScript 标准背后的技术委员会)的流程成为标准的一部分。

这个流程分为 5(0-4)个阶段。 随着提案得到越多的关注就越有可能被标准采纳,于是他们就继续通过各个阶段,最终在阶段 4 被标准正式采纳。

以下是4 个不同阶段的(打包的)预设:

  • babel-preset-stage-0
  • babel-preset-stage-1
  • babel-preset-stage-2
  • babel-preset-stage-3

注意 stage-4 预设是不存在的因为它就是上面的 es2015 预设。

以上每种预设都依赖于紧随的后期阶段预设。例如,babel-preset-stage-1 依赖 babel-preset-stage-2,后者又依赖 babel-preset-stage-3。.

使用的时候只需要安装你想要的阶段就可以了:

1
$ npm install --save-dev babel-preset-stage-2

配置

1
2
3
4
5
6
7
  {
"presets": [
"@babel/env",
+ "stage-2"
],
"plugins": []
}

配置 Babel

在项目的根目录下创建一个babel.config.js文件

  • babelrc: 只会影响本项目中的代码
  • babel.config.js 会影响整个项目中的代码,包括node_modules中的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const presets = [
[
"@babel/env",
{
targets: { // preset只会为目标浏览器中没有的功能加载转换插件
edge: "17",
chrome: "64",
firefox: "60",
safari: "11.1"
}
}
]
]

module.exports = { presets };

使用

在 package.json 配置命令行语句,它默认会去寻找跟根目录下的一个名为babel.config.js 的文件(或者 babelrc.js)

1
2
3
4
5
6
{
"scripts": {
"build": "babel src -d lib"
// 等于 babel src -d lib --config-file ./babel.config.js
}
}

Polyfill

Polyfill是对执行环境或者其它功能的一个补充

比如edge10浏览器中不支持ES7中的方法includes(),Polyfill 可以帮忙引入一个环境,补充方法

1
2
3
4
5
6
7
// 原来的代码
var hasTwo = [1, 2, 3].includes(2);

// 加了polyfill之后的代码
require("core-js/modules/es7.array.includes");
require("core-js/modules/es6.string.includes");
var hasTwo = [1, 2, 3].includes(2);

@babel/polyfill

Babel 用了优秀的 core-js 用作 polyfill,并且还有定制化的 regenerator 来让 generators(生成器)和 async functions(异步函数)正常工作

1
2
# 需要安装到开发环境,这是一个在源代码之前运行的 polyfill
$ npm i --save @babel/polyfill

因为使用的是 env presets,可以设置 useBuiltIns 选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const presets = [
[
"@babel/env",
{
targets: {
edge: "17",
chrome: "64",
firefox: "67",
safari: '11.1'
},
+ useBuiltIns: "usage" // 就只包括你需要的polyfill
}
]
]

module.exports = { presets }

但 @babel/polyfill 在Babel7.4.0以上已经不被推荐使用了。推荐使用core-js@3+@babel/preset-env然后设置@babel/preset-envcorejs选项为3.

按上述方法虽然可以正常转化 js,但会报错

1
2
3
4
5
6
WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option.

You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands:

npm install --save core-js@2 npm install --save core-js@3
yarn add core-js@2 yarn add core-js@3

解决办法是卸载掉@babel/polyfill, 然后重新安装core-js@版本号, 然后重新配置一些babel.config.js文件

  1. 安装core-js@3

    1
    npm i --save core-js@3
  2. 添加corejs选项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    const presets = [
    [
    "@babel/env",
    {
    targets: {
    edge: "17",
    chrome: "64",
    firefox: "67",
    safari: '11.1'
    },
    useBuiltIns: "usage",
    + corejs: 3
    }
    ]
    ]

    module.exports = { presets }

@babel/plugin-transform-runtime

为了实现 ECMAScript 规范的细节,Babel 会使用“助手”方法来保持生成代码的整洁。

由于这些助手方法可能会特别长并且会被添加到每一个文件的顶部,因此可以安装一些包把它们统一移动到一个单一的“运行时(runtime)”中去。

什么是助手?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Foo {
method() { }
}
// 编译成
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Foo = /*#__PURE__*/function () {
function Foo() {
_classCallCheck(this, Foo);
}

_createClass(Foo, [{
key: "method",
value: function method() {}
}]);

return Foo;
}();

安装包

1
npm i --save-dev @babel/plugin-transform-runtime

配置

1
const plugins = ["@babel/plugin-transform-runtime"]

编译后的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var Foo = /*#__PURE__*/function () {
function Foo() {
(0, _classCallCheck2["default"])(this, Foo);
}

(0, _createClass2["default"])(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();

基于环境自定义 Babel

Babel 将根据当前环境来开启 env 下的配置。

当前环境可以使用 process.env.BABEL_ENV 来获得。 如果 BABEL_ENV 不可用,将会替换成 NODE_ENV,并且如果后者也没有设置,那么缺省值是"development"

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": ["es2015"],
"plugins": [],
"env": {
"development": {
"plugins": [...]
},
"production": {
"plugins": [...]
}
}
}

plugins 与 presets 的执行顺序

可以同时使用多个 Plugin 和 Preset,此时,它们的执行顺序非常重要。

  • 先执行完所有 Plugin,再执行 Preset。
  • 多个 Plugin,按照声明次序顺序执行。
  • 多个 Preset,按照声明次序逆序执行。

比如 .babelrc配置如下,那么执行的顺序为:

  1. Plugin:transform-react-jsx、transform-async-to-generator
  2. Preset:es2016、es2015
1
2
3
4
5
6
7
8
9
10
11
// .babelrc
{
"plugins": [
"transform-react-jsx",
"transform-async-to-generator"
],
"presets": [
"es2015",
"es2016"
]
}

参考文章

建议改成: 读完这篇你还不懂Babel我给你寄口罩

Babel 用户手册